<?php
// +----------------------------------------------------------------------
// | B5Yii2CMF V3.0 [快捷通用基础管理开发平台]
// +----------------------------------------------------------------------
// | Author: 冰舞 <357145480@qq.com>
// +----------------------------------------------------------------------
declare (strict_types=1);

namespace api\utils\libs;


use api\utils\tools\PageUtils;
use api\utils\tools\QueryUtils;
use common\extend\exception\B5HttpException;
use common\helpers\ExportHelper;
use common\helpers\Functions;
use yii\db\ActiveQuery;
use yii\db\ActiveRecord;

/**
 * Trait CommonAction
 * 对常用的增删改查进行封装操作
 * list列表查询、export导出列表、info详情、add新增、edit编辑、delete删除、status修改状态
 * @property-read $model
 * @property-read bool $validate
 * @property-read array $noAction
 * @package api\utils\libs
 */
trait CommonAction
{
    /**
     *
     *公共列表action
     * 自定义条件和参数
     * @return array
     */
    public function actionList(): array
    {
        if (!$this->request->isPost && !$this->request->isGet) return $this->error('无效请求');
        if (in_array('list', $this->noAction ?? [])) return $this->error('无效操作');
        $params = $this->request->isPost ? $this->request->post() : $this->request->get();
        $queryResult = $this->listParse($params);
        $query = $queryResult['query'];
		$extend = $queryResult['extend'] ?? [];
        $params = $queryResult['params'];

        $isActive = $params['isActive']??0;// 是否indexAfter中的返回数据保持activeQuery
		$isTree = $params['isTree'] ?? 0; // 保持非前端后的兼容
        $isTotal = $params['isTotal'] ?? 0 || $isTree;//是否展示所有数据
        if (!$isTotal) { //是否分页
            $count = (clone $query)->count();

            if (!$isActive) $query = $query->asArray();
            $pages = new PageUtils('pageNum', 'pageSize');
            $list = $this->listAfter($query->offset($pages->getOffset())->limit($pages->getPageSize())->all());
            return $this->listReturn($pages->listFormat($list, $count, $extend));
        } else {
            if (!$isActive) $query = $query->asArray();
            $list = $this->listAfter($query->all());
            return $this->listReturn(['list'=>$list, ...$extend]);
        }
    }

    /**
     * isTree 是否查询所有数据 真为所有，假为分页
     * pageSize和 pageNum  分别为每页条数和页码，当isTree为假时有效
     * orderBy 为排序数组 [$field=>asc/desc,..]
     * field 查询的字段，可以为字符串或数组  'field1,field2,..' 或 [field1,field2,..]
     * orderByColumn和 isAsc 是前端列表自带的排序参数
     * alias 表的简称，不为空时，会在条件和查询字段等 拼接该值，主要为了当你在listQuery中需要join等联表查询时列名的冲突
     * @param array $params
     * @return array
     */
    private function listParse($params = []): array
    {
        $params = $this->listBefore($params);
        $debug = $params['debug'] ?? 0; // 是否输出sql语句
        $alias = $params['alias'] ?? '';// 表名简称

        $query = $this->listModel(); //创建查询构建器
        if ($alias) $query = $query->alias($alias);

        //处理表单查询条件
        try {
            $queryUtils = new QueryUtils($query, $params, $alias);
        } catch (B5HttpException $exception) {
            return $this->error($exception->getMessage());
        }
        $queryUtils->whereParse()->fieldParse()->orderParse();
        $query = $this->listQuery($queryUtils->getQuery()); //操作查询对象，可以进行语句处理以及数据权限处理
        $extend = [];
        if ($debug) $extend['sql'] = $query->createCommand()->getRawSql();
        return ['query' => $query, 'params' => $params, 'extend' => $extend];
    }
	
	/**
     * 创建查询构建器
     */
    protected function listModel(): ActiveQuery
    {
        return $this->model::find();
    }

    /**
     * 列表查询前的params处理，方便添加额外的条件或排序等
     * @param array $params
     * @return array
     */
    protected function listBefore(array $params): array
    {
        return $params;
    }

    /**
     * 列表查询语句处理，可以用来自定义以及数据权限处理
     * @param $query
     * @return ActiveQuery
     */
    protected function listQuery(ActiveQuery $query): ActiveQuery
    {
        return $query;
    }

    /**
     * 列表查询完的操作，方便对列表进行处理
     * @param array $list
     * @return array
     */
    protected function listAfter(array $list): array
    {
        return $list;
    }

    /**
     * 列表返回结果
     * @param array $result
     * @return array
     */
    protected function listReturn(array $result): array
    {
        return $this->success('', $result);
    }

    /**
     * 导出excel
     * @return array
     */
    public function actionExport(): array
    {
        if (!$this->request->isPost) return $this->error('无效请求');
        if (in_array('export', $this->noAction ?? [])) return $this->error('无效操作');
        $data = $this->request->post();
        $queryResult = $this->listParse($data);
        $query = $queryResult['query'];
        
        $params = $queryResult['params'];
        $asArray = $params['asArray'] ?? 0;
        if ($asArray) $query = $query->asArray();

        $list = $this->listAfter($query->all());
        $export_data = $this->exportBefore($list); // 处理导出数据
        try {
            $isFile = !!($data['_is_file_'] ?? true);
            $export = new ExportHelper($export_data);
            $export->saveFile = $isFile;
            $excel_res = $export->export();
            if ($isFile) {
                return $this->success('', ['path' => $excel_res, 'url' => Functions::getFileUrl($excel_res)]);
            }
        } catch (B5HttpException $exception) {
            return $this->error($exception->getMessage());
        }
    }

    /**
     * 导出配置，需配置导出字段 字段名=>标题
     * @param array $list
     * @return array
     */
    protected function exportBefore(array $list): array
    {
        return ['list' => $list, 'row' => [], 'fileName' => ''];
    }

    /**
     * 获取信息
     * @return array
     */
    public function actionInfo(): array
    {
        if (in_array('info', $this->noAction ?? [])) return $this->error('无效操作');
        $data = $this->request->get();
        if (!isset($data['id']) || !$data['id']) return $this->error('缺少主键条件');
        $model = $this->model::findOne($data['id']);
        if (!$model) return $this->error('信息不存在');
        return $this->infoAfter($model);
    }

    /**
     * 编辑信息查找后
     * @param ActiveRecord $model
     * @return array
     */
    protected function infoAfter(ActiveRecord $model): array
    {
        return $this->success('', ['info' => $model->toArray()]);
    }

    /**
     * 公共新增action
     * @return array
     */
    public function actionAdd(): array
    {
        if (!$this->request->isPost) return $this->error('无效请求');
        if (in_array('add', $this->noAction ?? [])) return $this->error('无效操作');
        $data = $this->request->post();
        if (isset($data['id'])) unset($data['id']);

        $data = $this->loadBefore($data, 'add');
        if (is_string($data)) return $this->error($data);

        $model = $this->modelAfter(new $this->model(), 'add');
        if (!$model->load($data, '')) return $this->error('无提交数据');

        if ($this->validate ?? false) {//验证前数据处理
            $validateBeforeRes = $this->validateBefore($model, 'add');
            if ($validateBeforeRes) return $this->error($validateBeforeRes);
            if (!$model->validate()) return $this->error(Functions::getModelError($model));
        }
        //数据处理
        $saveBeforeRes = $this->saveBefore($model, 'add');
        if ($saveBeforeRes) return $this->error($saveBeforeRes);
        $result = $model->save(false);
        if (!$result) return $this->error(Functions::getModelError($model, '保存失败'));
        $this->saveAfter($model, 'add');
        return $this->success('保存成功');
    }

    /**
     * 公共编辑action
     * @return array
     */
    public function actionEdit(): array
    {
        if (!$this->request->isPost) return $this->error('无效请求');
        if (in_array('edit', $this->noAction ?? [])) return $this->error('无效操作');
        $data = $this->request->post();
        if (!isset($data['id']) || !$data['id']) return $this->error('缺少编辑主键条件');

        $data = $this->loadBefore($data, 'edit');
        if (is_string($data)) return $this->error($data);

        $model = $this->model::findOne($data['id']);
        if (!$model) return $this->error('信息不存在');
        $model = $this->modelAfter($model, 'edit');
        $oldData = $model->toArray();

        if (!$model->load($data, '')) return $this->error('加载数据失败');
        if ($this->validate ?? false) { //验证前数据处理
            $validateBeforeRes = $this->validateBefore($model, 'edit', $oldData);
            if ($validateBeforeRes) return $this->error($validateBeforeRes);
            if (!$model->validate()) return $this->error(Functions::getModelError($model));
        }
        $saveBeforeRes = $this->saveBefore($model, 'edit', $oldData);//数据处理
        if ($saveBeforeRes) return $this->error($saveBeforeRes);

        $result = $model->save(false);
        if ($result === false) return $this->error('保存失败');
        if ($result) $this->saveAfter($model, 'edit', $oldData);
        return $this->success('保存成功');
    }

    /**
     * 公共改变状态
     * @return array
     */
    public function actionStatus(): array
    {
        if (!$this->request->isPost) return $this->error('无效请求');
        if (in_array('status', $this->noAction ?? [])) return $this->error('无效操作');

        $id = $this->request->post('id', '');
        if (!$id) return $this->error('缺少主键条件');

        $statusCondition = $this->statusCondition();
        $status = $this->request->post('status', '');
        $field = $this->request->post('field', 'status');
        $title = $this->request->post('status_text', '');
        if (!array_key_exists($field, $statusCondition)) return $this->error('无效的状态字段');
        if (!in_array($status, $statusCondition[$field])) return $this->error('无效的状态');

        $model = $this->model::findOne($id);
        if (!$model) return $this->error('信息不存在');
        if ($model[$field] == $status) return $this->success($title ?: '操作' . '成功');
        $oldData = $model->toArray();
        $model[$field] = $status;
        $saveBeforeRes = $this->saveBefore($model, $field, $oldData);
        if ($saveBeforeRes) return $this->error($saveBeforeRes);

        $result = $model->save(false);
        if ($result === false) return $this->error($title . '操作失败');
        $this->saveAfter($model, $field, $oldData);
        return $this->success($title . '操作成功');
    }

    /**
     * 修改状态的允许字段以及字段值
     * @return array
     */
    protected function statusCondition(): array
    {
        return ['status' => ['0', '1']];
    }

    /**
     * 添加、编辑 加载数据前，对数据处理
     * @param array $data
     * @param string $type
     * @return array|string
     */
    protected function loadBefore(array $data, string $type)
    {
        return $data;
    }

    /**
     * 添加、编辑 模型执行load之前的处理，例如进行场景指定
     * @param ActiveRecord $model
     * @param string $type
     * @return ActiveRecord
     */
    protected function modelAfter(ActiveRecord $model, string $type): ActiveRecord
    {
        return $model;
    }

    /**
     * 添加、编辑验证前的数据处理
     * @param ActiveRecord $model
     * @param string $type
     * @param array $oldData
     * @return string 返回错误字符串则返回该错误
     */
    protected function validateBefore(ActiveRecord $model, string $type, array $oldData = []): string
    {
        return '';
    }

    /**
     * 添加、编辑、状态修改前的数据处理
     * @param ActiveRecord $model
     * @param string $type
     * @param array $oldData
     * @return string
     */
    protected function saveBefore(ActiveRecord $model, string $type, array $oldData = []): string
    {
        return '';
    }

    /**
     * 添加、编辑、状态修改后的操作
     * @param ActiveRecord $model
     * @param string $type
     * @param array $oldData
     */
    protected function saveAfter(ActiveRecord $model, string $type, array $oldData = []): void
    {
    }

    /**
     * 公共删除
     * @return array
     */
    public function actionDelete(): array
    {
        if (!$this->request->isPost) return $this->error('无效请求');
        if (in_array('delete', $this->noAction ?? [])) return $this->error('无效操作');

        $ids = trim((string)$this->request->post('id', ''));
        if (!$ids) return $this->error('参数缺失');
        $ids = explode(',', $ids);
        if (count($ids) > 1) {
            $number = 0;
            foreach ($ids as $id) {
                if (!$id) continue;
                $info = $this->model::findOne($id);
                if (!$info) continue;
                $res = $this->deleteBefore($info, 'batch');//删除前
                if ($res) continue;
                if (method_exists($this->model,'softDelete')) {
                    $result = $info->softDelete();
                } else {
                    $result = $info->delete();
                }
                if ($result) {
                    $number++;
                    $this->deleteAfter($info, 'batch');
                }
            }
            return $this->success('批量删除完成，共删除' . $number . '条数据');
        } else {
            $id = $ids[0];
            if (!$id) return $this->error('参数缺失');
            $info = $this->model::findOne($id);
            if (!$info) return $this->error('信息不存在或数据已删除');

            $res = $this->deleteBefore($info, 'one'); //删除前
            if ($res) return $this->error($res);
            if (method_exists($this->model,'softDelete')) {
                $result = $info->softDelete();
            } else {
                $result = $info->delete();
            }
            if ($result === false) return $this->error('删除失败');
            if ($result) $this->deleteAfter($info, 'one');//删除后操作
            return $this->success('删除成功');
        }
    }

    /**
     * 删除后的操作
     * @param ActiveRecord $model
     * @param string $type
     */
    protected function deleteAfter(ActiveRecord $model, string $type): void
    {
    }

    /**
     * 删除前操作
     * @param ActiveRecord $model
     * @param string $type
     * @return string
     */
    protected function deleteBefore(ActiveRecord $model, string $type): string
    {
        return '';
    }
}